package com.lincoln.framework;

import com.lincoln.framework.bean.NettyContext;
import com.lincoln.framework.factories.ProtocolFactory;
import com.lincoln.framework.factories.ProtocolFactoryBean;
import com.lincoln.framework.interfaces.BaseDeviceInfo;
import com.lincoln.framework.interfaces.BaseExecute;
import com.lincoln.framework.interfaces.BaseProtocol;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.UUID;

/**
 * 事件响应
 *
 * @author lincoln
 */
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    private static Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);

    /**
     * 内部为了找到客户端使用的客户端id
     */
    private String clientId = null;

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        try {
            BaseProtocol protocol = (BaseProtocol) msg;
            if (!protocol.verify()) {
                return;
            }
            logger.info("收到来自帧:" + protocol.toString());
            BaseDeviceInfo deviceInfo = NettyContext.getDevice();
            //时时更新ip与端口号
            deviceInfo.setIp(((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress());
            deviceInfo.setPort(((InetSocketAddress) ctx.channel().remoteAddress()).getPort());
            //请求次数+1
            deviceInfo.getAttributes().put(BaseDeviceInfo.REQUEST_TIME, (int) (deviceInfo.getAttributes().get(BaseDeviceInfo.NO_LOGIN_REQUEST_TIME) == null ? 0 : deviceInfo.getAttributes().get(BaseDeviceInfo.REQUEST_TIME)) + 1);
            //获取对应协议
            ProtocolFactoryBean protocolFactory = ProtocolFactory.getProtocol(deviceInfo.getDeviceType(), deviceInfo.getProtocolType());
            if (!deviceInfo.isLogin()) {
                //未登录请求次数+1
                deviceInfo.getAttributes().put(BaseDeviceInfo.NO_LOGIN_REQUEST_TIME, (int) (deviceInfo.getAttributes().get(BaseDeviceInfo.NO_LOGIN_REQUEST_TIME) == null ? 0 : deviceInfo.getAttributes().get(BaseDeviceInfo.NO_LOGIN_REQUEST_TIME)) + 1);
                if (protocolFactory == null && NettyServer.getNettyServer().getDefaultExecutes().size() > 0) {
                    BaseExecute execute = NettyServer.getNettyServer().getDefaultExecutes().get(protocol.getCmdText());
                    if (execute != null) {
                        execute(execute, protocol);
                        return;
                    }
                }
            }

            // TODO: 2018/11/30 此节点要增加灵活的执行器,决定是否要继续向下执行

            BaseExecute execute = null;
            if (protocolFactory != null) {
                execute = protocolFactory.getExecute(protocol.getCmdText());
            }
            if (execute == null) {
                // TODO: 2018/11/30 此节点要增加灵活的执行器,加入一些默认的执行器
                logger.info("没有对应的解析代码!");
            } else {
                execute(execute, protocol);
            }
        } catch (Exception e) {
            logger.error("异常:", e);
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }

    /**
     * 执行指令
     *
     * @param execute
     * @param protocol
     * @return
     */
    private boolean execute(BaseExecute execute, BaseProtocol protocol) {
        if (execute.decoder(protocol)) {
            boolean res = execute.execute(protocol);
            if (res) {
                logger.info("执行完成!");
                return true;
            } else {
                logger.info("执行失败!");
                return false;
            }
        } else {
            logger.info("decode未通过!");
            return false;
        }
    }

    /**
     * 心跳机制
     *
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            //超时读取
            if (event.state() == IdleState.READER_IDLE) {
                logger.info("心跳超时,服务器主动关闭");
                ctx.channel().close();
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        if (cause instanceof IOException) {
            //do nothing
        } else {
            logger.error("", cause);
        }
        ctx.close();
    }


    /**
     * 新客户端连接后触发
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws IllegalAccessException, InstantiationException {
        logger.info("新客户端连接,等待验证:");
        if (clientId == null) {
            clientId = UUID.randomUUID().toString();
            BaseDeviceInfo deviceInfo = NettyServer.getNettyServer().getDeviceInfo().newInstance();
            deviceInfo.setClientId(clientId);
            deviceInfo.setIp(((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress());
            deviceInfo.setPort(((InetSocketAddress) ctx.channel().remoteAddress()).getPort());
            deviceInfo.setChannelHandlerContext(ctx);
            NettyContext.addClient(clientId, deviceInfo);
            NettyContext.addNoLogin(clientId);
            NettyContext.setDevice(deviceInfo);
            logger.info("未认证的新客户端进入!");
        }
        ctx.fireChannelActive();
    }

    /**
     * 连接关闭后触发
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        if (null != clientId && !"".equals(clientId) && null != NettyContext.getClient(clientId)) {
            NettyContext.getDevice().logout();
            NettyContext.removeClient(clientId);
            NettyContext.remoceNettyContextBean();
        }
    }

    /**
     * 发送消息
     *
     * @param protocol
     */
    public void sendMsg(BaseProtocol protocol) {
        NettyContext.getDevice().sendMsg(protocol);
    }
}
